﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace Simulated_Annealing
{
    //the VRP file characterizes the VRP problem elements from the TSPLIb input file 
    public class VRP
    {
        
        public int numnodes;               //Number of nodes
        public double[][] costmatrix;        //OD matrix
        public double[] demandarray;        //Demand array
        public double capacity;             //Vehicle capacity
        public double optimal_solution;     //Optimal solution
        public bool error = false;         //Program error status
        public double lambda;               //Lambda for calculating saving matrix
        public double objective;            //Problem objective value, i.e, total travel cost
        public List<List<int>> routes;     //Nested list to store vehicle routes
        public List<double> route_capacity;
        public List<int> missingnodes;
        public string routes_info;
        public string objective_info;
        public string node_info;
        public double gap;

        public VRP(VRP vrp)
        {
            this.numnodes = vrp.numnodes;
            this.capacity = vrp.capacity;
            this.optimal_solution = vrp.optimal_solution;
            this.error = vrp.error;
            this.lambda = vrp.lambda;
            this.objective = vrp.objective;
            this.demandarray = new double[vrp.demandarray.Length];

            Array.Copy(vrp.demandarray, this.demandarray, vrp.demandarray.Length);

            this.routes = new List<List<int>>();

            foreach (List<int> route1 in vrp.routes.Select(route => route.ToList()))
            {
                this.routes.Add(route1);
            }
            

            costmatrix = new double[vrp.costmatrix.Length][];
            for (int i = 0; i < costmatrix.Length; i++)
            {
                costmatrix[i] = new double[costmatrix.Length];
                Array.Copy(vrp.costmatrix[i], costmatrix[i], vrp.costmatrix[i].Length);
            }

          
           //this.missingnodes = vrp.missingnodes;

           this.routes_info=vrp.routes_info;
           this.objective_info=vrp.objective_info;
           this.node_info=vrp.node_info;
           this.gap=vrp.gap;
           this.missingnodes=new List<int>();
            
        }



        public VRP()
        {

        }


        public double routecapacity(List<int> route, VRP network) //we use this function to calculate the route capacity
        {
            return route.Aggregate<int, double>(0, (current, t) => current + network.demandarray[t]);
        }

        public double routelength(List<int> route, VRP network)//this will automatically calculate the length of one route
        {
            double length = 0;

            for (int j = 0; j < route.Count - 1; j++)
            {

                length = length + network.costmatrix[route[j]][route[j + 1]];

                if (j == 0)
                {
                    length = length +network.costmatrix[0][route[j]];
                }
                if (j == route.Count - 2)
                {
                    length = length +network.costmatrix[0][route[j + 1]];
                }

            }
            return length;
        }
        public void route_clean(VRP network) //this function will automatically delete the empty routes
        {
            for (int i = 0; i < network.routes.Count; )
            {
                if (network.routes[i].Count == 0)
                {
                    network.routes.RemoveAt(i);
                    i = 0;
                }
                else 
                {
                    i++;
                }
            }
        }

        public double[][] ODmatrix(TSPLIBParser myparser)
        {
            int numnodes = myparser.Dimension;
            double[][] costmatrix = new double[numnodes][];

            for (int i = 0; i < costmatrix.Length; i++)
            {
                costmatrix[i] = new double[numnodes];
            }

        double[,] vertices = myparser.Vertices;
            for (int i = 0; i < numnodes; i++)
            {
                for (int j = 0; j < numnodes; j++)
                {

                    if (i != j)
                    {
                        double x1 = vertices[i, 0];
                        double y1 = vertices[i, 1];
                        double x2 = vertices[j, 0];
                        double y2 = vertices[j, 1];
                        costmatrix[i][j] = Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
                    }
                    else
                    {
                        costmatrix[i][j] = 1000000;
                    }

                }
            }
            return costmatrix;
        }

        

        public double totalcost(List<List<int>> routes, VRP network)
        {
            double objective = 0;

            int numofroutes = routes.Count;
            for (int i = 0; i < numofroutes; i++)
            {
                List<int> singleroute = new List<int>();
                singleroute = routes[i];

                if (singleroute.Count == 1)
                {
                    objective = objective+2*network.costmatrix[0][singleroute[0]];
                }
                else 
                {
                    for (int j = 0; j < singleroute.Count - 1; j++)
                    {

                        objective = objective + network.costmatrix[singleroute[j]][singleroute[j + 1]];

                        if (j == 0)
                        {
                            objective = objective + network.costmatrix[0][singleroute[j]];
                        }
                        if (j == singleroute.Count - 2)
                        {
                            objective = objective + network.costmatrix[0][singleroute[j + 1]];
                        }

                    }
                }                                
            }

            network.objective = objective;
            return objective;
        }

        public static List<double> find_temp(VRP saving, Random random)
        {
            double T_s = 0;
            double T_f = saving.totalcost(saving.routes, saving);
            VRP neighbor1 = new VRP(saving);
            VRP neighbor2 = new VRP(saving);
            double T_s_old = 0;
            double T_s_new = 1;
            int[,] routepair = Neighborhood.routepairlist(saving, random);
            List<Nodes> empty_nodes = new List<Nodes>();
            while (Math.Abs(T_s_old - T_s_new) / (T_s_new) > 0.1)
            {
                T_s_old = T_s;
                for (int i = 0; i < routepair.GetLength(0); i++)
                {
                    for (int j = 0; j <= 1; j++)
                    {
                        double cost = 0;
                        if (j == 0)
                        {
                            int route_remove = routepair[i, 0];
                            int route_insert = routepair[i, 1];
                            neighbor2 = new VRP(Neighborhood.best_move(neighbor2, route_remove, route_insert, empty_nodes));
                            neighbor2.route_clean(neighbor2);
                            cost = Math.Abs(neighbor1.totalcost(neighbor1.routes, neighbor1) - neighbor2.totalcost(neighbor2.routes, neighbor2));
                            if (cost > T_s_new)
                            {
                                T_s_new = cost;
                            }
                            if (T_f > cost && cost > 20)
                            {
                                T_f = cost;
                            }
                        }
                        else
                        {
                            int route_remove = routepair[i, 1];
                            int route_insert = routepair[i, 0];
                            neighbor2 = new VRP(Neighborhood.best_move(neighbor2, route_remove, route_insert, empty_nodes));
                            neighbor2.route_clean(neighbor2);
                            cost = Math.Abs(neighbor1.totalcost(neighbor1.routes, neighbor1) - neighbor2.totalcost(neighbor2.routes, neighbor2));
                            if (cost > T_s_new)
                            {
                                T_s_new = cost;
                            }
                            if (T_f > cost && cost > 20)
                            {
                                T_f = cost;
                            }
                        }
                    }
                }
                neighbor1 = new VRP(neighbor2);
                routepair = Neighborhood.routepairlist(neighbor1, random);
                T_s = T_s_new;
               
            }
            List<double> temperatures = new List<double> { T_f, T_s };
            return temperatures; 
            }


        public static int find_Nfeas(VRP saving, Random random)
        {
            int[,] routepair = Neighborhood.routepairlist(saving, random);
            int counter = 0;
            for (int i = 0; i < routepair.GetLength(0); i++)
            {
                for (int j = 0; j <= 1; j++)
                {
                    if (j == 0)
                    {
                        int route_remove = routepair[i, 0];
                        int route_insert = routepair[i, 1];
                        for (int k = 0; k < saving.routes[route_remove].Count; k++)
                        {
                            int node = saving.routes[route_remove][k];
                            if (saving.routecapacity(saving.routes[route_insert], saving) + saving.demandarray[node] < saving.capacity)
                            {
                                counter = counter + saving.routes[route_insert].Count;
                            }
                        }
                    }
                    else
                    {
                        int route_remove = routepair[i, 1];
                        int route_insert = routepair[i, 0];
                        for (int k = 0; k < saving.routes[route_remove].Count; k++)
                        {
                            int node = saving.routes[route_remove][k];
                            if (saving.routecapacity(saving.routes[route_insert], saving) + saving.demandarray[node] < saving.capacity)
                            {
                                counter = counter + saving.routes[route_insert].Count;
                            }
                        }
                    }
                }
            }
            return counter;

        }
    }
}